#ifndef __H_SESSION__
#define __H_SESSION__
#include "logger.hpp"
#include <unordered_map>
#include <functional>
#include <websocketpp/server.hpp>
#include <websocketpp/config/asio_no_tls.hpp>
typedef enum{UNLOGIN,LOGIN} ss_statu;
using wsserver_t = websocketpp::server<websocketpp::config::asio>;
class session
{
    public:
        session(uint64_t ssid):_ssid(ssid),_statu(UNLOGIN){DLOG("SESSION %p 被创建！！",this);}
        ~session() {DLOG("SESSION %p 被释放！！",this);}
        void set_user(uint64_t uid) {_uid = uid;}
        uint64_t ssid(){return _ssid;}
        void set_statu(ss_statu statu) {_statu = statu;}
        uint64_t get_user() {return _uid;}
        bool is_login() {return _statu == LOGIN;}
        void set_timer(const wsserver_t::timer_ptr &tp) {_tp = tp;}
        wsserver_t::timer_ptr& get_timer() {return _tp;}
    private:
        uint64_t _ssid; //会话ID
        uint64_t _uid; //session对应的用户ID
        ss_statu _statu; //用户状态：未登录/已登录
        wsserver_t::timer_ptr _tp; //session关联的定时器
};

#define TIMER_FOREVER -1
using session_ptr = std::shared_ptr<session>;
class session_manager
{
    public:
        session_manager(wsserver_t* server) : _server(server){DLOG("session管理对象创建成功！！");}
        ~session_manager(){DLOG("session管理对象已被释放");}
        session_ptr create_session(uint64_t uid , ss_statu statu)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            session_ptr ret(new session(_next_ssid));
            ret->set_statu(statu);
            ret->set_user(uid);
            _session.insert(std::make_pair(_next_ssid,ret));
            ++_next_ssid;
            return ret;
        }
        session_ptr get_session(uint64_t ssid)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto iter = _session.find(ssid);
            if(iter == _session.end()) return session_ptr();
            return iter->second;
        }
        void append_session(uint64_t ssid,session_ptr ssp)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _session.insert(std::make_pair(ssid,ssp));
        }
        void set_session_expire_time(uint64_t ssid , uint64_t ms)
        {
            //依赖于websocketpp的定时器来完成session生命周期的管理
            //在http通信的时候(登录/注册) session应该具备生命周期，指定时间无通信后删除
            //在建立websocket长连接之后，session应该是永久存在的
            //登录之后，创建session，session需要在指定时间无通信后删除
            //但是进入游戏大厅，或者游戏房间，这个session就应该永久存在
            //等到退出游戏大厅，或者游戏房间，这个session应该被重新设置为临时，在长时间无通信后被删除
            auto iter = _session.find(ssid);
            if(iter == _session.end()) return;
            session_ptr ssp = iter->second;
            wsserver_t::timer_ptr tp = ssp->get_timer();
            if(tp.get() == nullptr && ms == TIMER_FOREVER)
            {
                //1、在session永久存在的情况下，设置永久存在
                return;
            }else if(tp.get() == nullptr && ms != TIMER_FOREVER){
                //2、在session永久存在的情况下，设置指定时间之后被删除的定时任务
                tp = _server->set_timer(ms,std::bind(&session_manager::remove_session,this,ssp->ssid()));
                ssp->set_timer(tp);
                return;
            }else if(tp.get() != nullptr && ms == TIMER_FOREVER){
                //3、在session设置了定时删除的情况下，将session设置为永久存在
                //删除定时任务————stready_timer删除定时任务会导致任务直接被执行
                tp->cancel();
                ssp->set_timer(wsserver_t::timer_ptr());
                //因此重新给session管理器中，添加一个session信息，且添加的时候需要使用定时器，而不是立即添加
                _server->set_timer(0,std::bind(&session_manager::append_session,this,ssid,ssp));
            }else if(tp.get() != nullptr && ms != TIMER_FOREVER){
                //4、在session设置了定时删除的情况下，将session重置删除时间
                tp->cancel();
                _server->set_timer(0,std::bind(&session_manager::append_session,this,ssid,ssp));
                tp = _server->set_timer(ms,std::bind(&session_manager::remove_session,this,ssid));
                ssp->set_timer(tp);
            }
        }
        void remove_session(uint64_t ssid)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if(_session.count(ssid))_session.erase(ssid);
        }
    private:
        uint64_t _next_ssid;
        std::mutex _mutex;
        std::unordered_map<uint64_t,session_ptr> _session;
        wsserver_t* _server;
};

#endif